package com.tian.service.impl;

import com.github.binarywang.wxpay.exception.WxPayException;
import com.tian.convert.ChargeBalanceRecordConvertUtil;
import com.tian.dto.ChargeBalanceRecordDto;
import com.tian.dto.PayNotifyReqDto;
import com.tian.dto.WxPrePayReqDto;
import com.tian.entity.ChargeBalanceRecord;
import com.tian.entity.User;
import com.tian.enums.PayTypeEnum;
import com.tian.enums.ResultCode;
import com.tian.enums.WxPayStatusEnum;
import com.tian.mapper.ChargeBalanceRecordMapper;
import com.tian.mapper.UserMapper;
import com.tian.service.ChargeBalanceRecordService;
import com.tian.util.CommonResult;
import com.tian.util.ConstantUtil;
import com.tian.util.RedisConstantPre;
import com.tian.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年05月12日 16:10
 * 博客地址：<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 微信支付
 */
@Slf4j
@Service
public class ChargeBalanceRecordServiceImpl implements ChargeBalanceRecordService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private ChargeBalanceRecordMapper chargeBalanceRecordMapper;
    @Resource
    private RedissonClient redissonClient;

    @Transactional(rollbackFor = WxPayException.class)
    @Override
    public CommonResult<Boolean> createRecord(WxPrePayReqDto wxPrePayReqDto) {
        User user = userMapper.selectByPrimaryKey(wxPrePayReqDto.getUserId());
        if (user == null) {
            return CommonResult.failed(ResultCode.PARAMETER_ERROR);
        }
        //微信授权后+手机号验证码
        if (user.getAttestationStatus() != 1) {
            return CommonResult.failed(ResultCode.UN_AUTHENTICATION);
        }
        ChargeBalanceRecord chargeBalanceRecord = new ChargeBalanceRecord();
        chargeBalanceRecord.setUserId(wxPrePayReqDto.getUserId());
        chargeBalanceRecord.setOrderNo(wxPrePayReqDto.getOrderNo());
        chargeBalanceRecord.setPayType(PayTypeEnum.WX_PAY.getPayType());
        chargeBalanceRecord.setStatus(WxPayStatusEnum.INIT.getStatus());
        chargeBalanceRecord.setBalance(wxPrePayReqDto.getAmount().multiply(new BigDecimal(ConstantUtil.MONEY)).intValue());
        chargeBalanceRecord.setCreateTime(new Date());
        int flag = chargeBalanceRecordMapper.insert(chargeBalanceRecord);
        if (flag == 1) {
            return CommonResult.success(Boolean.TRUE);
        }
        return CommonResult.failed(ResultCode.FAILED);
    }

    @Override
    public CommonResult<ChargeBalanceRecordDto> selectByOrderNo(String orderNo) {
        ChargeBalanceRecord chargeBalanceRecord = chargeBalanceRecordMapper.selectByOrderNo(orderNo);
        if (chargeBalanceRecord == null) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        }
        return CommonResult.success(ChargeBalanceRecordConvertUtil.convert(chargeBalanceRecord));
    }

    @Override
    public CommonResult<Boolean>  queryPayOrderJob() {
        int count = chargeBalanceRecordMapper.selectByStatus(WxPayStatusEnum.INIT.getStatus());
        if (count == 0) {
            return CommonResult.success();
        }
        int pageSize = 50;
        if (count < pageSize) {
            List<ChargeBalanceRecord> chargeBalanceRecordList = chargeBalanceRecordMapper.listPage(WxPayStatusEnum.INIT.getStatus(), 0, count);
            doPayOrderStatus(chargeBalanceRecordList);
            return CommonResult.success();
        }
        int length = count / pageSize;
        for (int i = 0; i < length; i++) {
            List<ChargeBalanceRecord> chargeBalanceRecordList = chargeBalanceRecordMapper.listPage(WxPayStatusEnum.INIT.getStatus(), i * pageSize, count);
            doPayOrderStatus(chargeBalanceRecordList);
        }
        return CommonResult.success();
    }

    private void doPayOrderStatus(List<ChargeBalanceRecord> chargeBalanceRecordList) {
        for (ChargeBalanceRecord chargeBalanceRecord : chargeBalanceRecordList) {
            //调微信 支付订单查询接口
            // TODO: 2023/10/7 根据返回结果判断支付订单状态来设置chargeBalanceRecord的状态
            chargeBalanceRecordMapper.updateByPrimaryKey(chargeBalanceRecord);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<Boolean> wxPayNotify(PayNotifyReqDto payNotifyReqDto) throws Exception {
        if (payNotifyReqDto == null || payNotifyReqDto.getRecordId() == null || StringUtil.isBlank(payNotifyReqDto.getThirdTradeNo())) {
            return CommonResult.failed(ResultCode.PARAMETER_EMPTY);
        }

        ChargeBalanceRecord chargeBalanceRecord = chargeBalanceRecordMapper.selectByPrimaryKey(payNotifyReqDto.getRecordId());
        if (chargeBalanceRecord == null) {
            log.error("支付回调通知，id：" + payNotifyReqDto.getRecordId());
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        }

        //分布式锁锁住该用户账号---
        String key = RedisConstantPre.BALANCE_LOCK_PRE + payNotifyReqDto.getRecordId();
        RLock rLock = redissonClient.getLock(key + chargeBalanceRecord.getUserId());

        rLock.lock();
        try {
            User user = userMapper.selectByPrimaryKey(chargeBalanceRecord.getUserId());

            //修改状态
            chargeBalanceRecord.setThirdTradeNo(payNotifyReqDto.getThirdTradeNo());
            chargeBalanceRecord.setStatus(WxPayStatusEnum.SUCCESS.getStatus());
            int flag = chargeBalanceRecordMapper.updateByPrimaryKey(chargeBalanceRecord);
            if (flag == 1) {
                //给用户账户添加余额
                user.setBalance(user.getBalance() + chargeBalanceRecord.getBalance());
                flag = userMapper.updateByPrimaryKey(user);
                if (flag == 0) {
                    throw new Exception("更新用户余额失败");
                }
                return CommonResult.success(Boolean.TRUE);
            }
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        } finally {
            rLock.unlock();
        }
    }
}
